package com.zbkj.service.service.impl;


import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.zbkj.dal.base.constants.RedisConstants;
import com.zbkj.dal.base.constants.SysConfigConstants;
import com.zbkj.dal.base.enums.ExpressTraceOpCode;
import com.zbkj.dal.base.enums.OrderInvoceStatus;
import com.zbkj.dal.common.exception.CrmebException;
import com.zbkj.dal.common.utils.RedisUtil;
import com.zbkj.dal.common.utils.RestTemplateUtil;
import com.zbkj.dal.dao.ExpressDao;
import com.zbkj.dal.dao.ExpressTraceMapper;
import com.zbkj.dal.entity.express.Express;
import com.zbkj.dal.entity.express.ExpressTrace;
import com.zbkj.dal.entity.order.OrderInvoice;
import com.zbkj.dal.pojo.vo.LogisticsResultListVo;
import com.zbkj.dal.pojo.vo.LogisticsResultVo;
import com.zbkj.dal.pojo.vo.OnePassLogisticsQueryVo;
import com.zbkj.service.service.LogisticService;
import com.zbkj.service.service.OnePassService;
import com.zbkj.service.service.OrderInvoiceService;
import com.zbkj.service.service.SystemConfigService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


/**
 * ExpressServiceImpl 接口实现
 * +----------------------------------------------------------------------
 * | CRMEB [ CRMEB赋能开发者，助力企业发展 ]
 * +----------------------------------------------------------------------
 * | Copyright (c) 2016~2023 https://www.crmeb.com All rights reserved.
 * +----------------------------------------------------------------------
 * | Licensed CRMEB并不是自由软件，未经许可不能去掉CRMEB相关版权
 * +----------------------------------------------------------------------
 * | Author: CRMEB Team <admin@crmeb.com>
 * +----------------------------------------------------------------------
 */
@Service
public class LogisticsServiceImpl implements LogisticService {

    @Autowired
    private SystemConfigService systemConfigService;
    @Autowired
    private RestTemplateUtil restTemplateUtil;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private OnePassService onePassService;
    @Autowired
    private ExpressTraceMapper expressTraceMapper;
    @Autowired
    private ExpressDao expressDao;


    @Override
    public LogisticsResultVo info(OrderInvoice orderInvoice) {
        LambdaQueryWrapper<Express> qw = Wrappers.lambdaQuery(Express.class);
        qw.eq(Express::getCode, orderInvoice.getExpressCode());
        Express express = expressDao.selectOne(qw);
        Assert.notNull(express, "快递公司不存在");

        List<ExpressTrace> expressTraces = this.findExpressTraceByMailNoIn(Collections.singleton(orderInvoice.getTrackingNumber()));

        List<LogisticsResultListVo> collect = expressTraces.stream().map(it -> {
            LogisticsResultListVo logisticsResultListVo = new LogisticsResultListVo();
            logisticsResultListVo.setTime(it.getCreateTime());
            logisticsResultListVo.setStatus(it.getRemark());
            logisticsResultListVo.setStatusText(ExpressTraceOpCode.of(it.getOpCode()).getCorrespondingLogisticsStatus().getDesc());
            logisticsResultListVo.setRemark(it.getRemark());
            return logisticsResultListVo;
        }).collect(Collectors.toList());

        LogisticsResultVo logisticsResultVo = new LogisticsResultVo();
        logisticsResultVo.setNumber(orderInvoice.getTrackingNumber());
        logisticsResultVo.setType(orderInvoice.getExpressOrderType());
        logisticsResultVo.setList(collect);
        logisticsResultVo.setOrderNo(orderInvoice.getOrderNo());
        logisticsResultVo.setStatus(orderInvoice.getStatus());
        logisticsResultVo.setStatusText(OrderInvoceStatus.codeOf(orderInvoice.getStatus()).getDesc());
        logisticsResultVo.setExpName(orderInvoice.getExpressName());
        logisticsResultVo.setLogo(express.getLogo());
        return logisticsResultVo;
    }

    @Override
    public LogisticsResultVo infoAndLastTraceOnly(OrderInvoice orderInvoice) {
        LambdaQueryWrapper<Express> qw = Wrappers.lambdaQuery(Express.class);
        qw.eq(Express::getCode, orderInvoice.getExpressCode());
        Express express = expressDao.selectOne(qw);
        Assert.notNull(express, "快递公司不存在");
        LambdaQueryWrapper<ExpressTrace> queryWrapper = Wrappers.lambdaQuery(ExpressTrace.class);
        queryWrapper.eq(ExpressTrace::getMailNo, orderInvoice.getTrackingNumber());
        queryWrapper.orderByDesc(ExpressTrace::getId);
        queryWrapper.last("limit 1");
        ExpressTrace expressTraces = expressTraceMapper.selectOne(queryWrapper);
        //
        LogisticsResultListVo logisticsResultListVo = new LogisticsResultListVo();
        logisticsResultListVo.setTime(expressTraces.getCreateTime());
        logisticsResultListVo.setStatus(expressTraces.getRemark());
        logisticsResultListVo.setStatusText(ExpressTraceOpCode.of(expressTraces.getOpCode()).getCorrespondingLogisticsStatus().getDesc());
        logisticsResultListVo.setRemark(expressTraces.getRemark());
        //
        LogisticsResultVo logisticsResultVo = new LogisticsResultVo();
        logisticsResultVo.setNumber(orderInvoice.getTrackingNumber());
        logisticsResultVo.setType(orderInvoice.getExpressOrderType());
        logisticsResultVo.setLast(logisticsResultListVo);
        logisticsResultVo.setStatus(orderInvoice.getStatus());
        logisticsResultVo.setStatusText(OrderInvoceStatus.codeOf(orderInvoice.getStatus()).getDesc());
        logisticsResultVo.setExpName(orderInvoice.getExpressName());
        logisticsResultVo.setLogo(express.getLogo());
        return logisticsResultVo;
    }

    @Override
    public List<LogisticsResultVo> infoListAndLastTraceOnly(List<OrderInvoice> orderInvoice) {
        List<String> expressCode = orderInvoice.stream().map(OrderInvoice::getExpressCode).collect(Collectors.toList());
        LambdaQueryWrapper<Express> qw = Wrappers.lambdaQuery(Express.class);
        qw.in(Express::getCode, expressCode);
        Express express = expressDao.selectOne(qw);
        Assert.notNull(express, "快递公司不存在");
        List<String> trackingNumber = orderInvoice.stream().map(OrderInvoice::getTrackingNumber).collect(Collectors.toList());
        List<ExpressTrace> expressTraces = this.findExpressTraceByMailNoIn(trackingNumber);
        Map<String, Optional<ExpressTrace>> traceMap = expressTraces.stream().collect(Collectors.groupingBy(ExpressTrace::getMailNo, Collectors.maxBy(Comparator.comparingInt(ExpressTrace::getId))));
        return orderInvoice.stream().map(oi -> {
            LogisticsResultVo logisticsResultVo = new LogisticsResultVo();
            logisticsResultVo.setExpressId(oi.getId());
            logisticsResultVo.setNumber(oi.getTrackingNumber());
            logisticsResultVo.setType(oi.getExpressOrderType());
            logisticsResultVo.setStatus(oi.getStatus());
            logisticsResultVo.setStatusText(OrderInvoceStatus.codeOf(oi.getStatus()).getDesc());
            logisticsResultVo.setExpName(oi.getExpressName());
            logisticsResultVo.setLogo(express.getLogo());

            traceMap.getOrDefault(oi.getTrackingNumber(), Optional.empty()).ifPresent(trace -> {
                LogisticsResultListVo logisticsResultListVo = new LogisticsResultListVo();
                logisticsResultListVo.setTime(trace.getCreateTime());
                logisticsResultListVo.setStatus(trace.getRemark());
                logisticsResultListVo.setStatusText(ExpressTraceOpCode.of(trace.getOpCode()).getCorrespondingLogisticsStatus().getDesc());
                logisticsResultListVo.setRemark(trace.getRemark());
                logisticsResultVo.setLast(logisticsResultListVo);
            });
            return logisticsResultVo;
        }).collect(Collectors.toList());
    }

    private List<ExpressTrace> findExpressTraceByMailNoIn(Collection<String> mailNo) {
        LambdaQueryWrapper<ExpressTrace> queryWrapper = Wrappers.lambdaQuery(ExpressTrace.class);
        queryWrapper.in(ExpressTrace::getMailNo, mailNo);
        queryWrapper.orderByDesc(ExpressTrace::getId);
        return expressTraceMapper.selectList(queryWrapper);
    }


    /**
     * 快递
     *
     * @param expressNo String 物流单号
     * @param type      String 快递公司字母简写：不知道可不填 95%能自动识别，填写查询速度会更快 https://market.aliyun.com/products/56928004/cmapi021863.html#sku=yuncode15863000015
     * @param com       快递公司编号
     * @param phone     手机号
     * @return LogisticsResultVo
     */
    private LogisticsResultVo info(String expressNo, String type, String com, String phone) {
        LogisticsResultVo resultVo = new LogisticsResultVo();
        String redisKey = RedisConstants.LOGISTICS_KEY + expressNo;
        JSONObject result = getCache(redisKey);
        if (ObjectUtil.isNotNull(result)) {
            return JSONObject.toJavaObject(result, LogisticsResultVo.class);
        }
        // 顺丰请输入单号 : 收件人或寄件人手机号后四位。例如：123456789:1234
        if (StrUtil.isNotBlank(com) && com.equals("shunfengkuaiyun")) {
            expressNo = expressNo.concat(":").concat(StrUtil.sub(phone, 7, -1));
        }
        String logisticsType = systemConfigService.getValueByKeyException(SysConfigConstants.LOGISTICS_QUERY_TYPE);
        if (logisticsType.equals("1")) {// 平台查询
            OnePassLogisticsQueryVo queryVo = onePassService.exprQuery(expressNo, com);
            if (ObjectUtil.isNull(queryVo)) {
                resultVo.setNumber(expressNo);
                resultVo.setExpName(com);
                return resultVo;
            }
            // 一号通vo转公共返回vo
            resultVo = queryToResultVo(queryVo);
            String jsonString = JSONObject.toJSONString(resultVo);
            saveCache(redisKey, JSONObject.parseObject(jsonString));
        }
        if (logisticsType.equals("2")) {// 阿里云查询
            String appCode = systemConfigService.getValueByKey(SysConfigConstants.LOGISTICS_QUERY_ALIYUN_CODE);
            String url = StrUtil.format(SysConfigConstants.LOGISTICS_QUERY_ALIYUN_URL, expressNo);
            if (StringUtils.isNotBlank(type)) {
                url = url.concat(StrUtil.format("&type={}", type));
            }
            HashMap<String, String> header = new HashMap<>();
            header.put("Authorization", "APPCODE " + appCode);

            JSONObject data = restTemplateUtil.getData(url, header);
            checkResult(data);
            //把数据解析成对象返回到前端
            result = data.getJSONObject("result");
            saveCache(redisKey, result);
            resultVo = JSONObject.toJavaObject(result, LogisticsResultVo.class);
        }
        return resultVo;
    }

    /**
     * 一号通vo转公共返回vo
     */
    private LogisticsResultVo queryToResultVo(OnePassLogisticsQueryVo queryVo) {
        LogisticsResultVo resultVo = new LogisticsResultVo();
        resultVo.setNumber(queryVo.getNum());
        resultVo.setExpName(queryVo.getCom());
//        resultVo.setIsSign(queryVo.getIscheck());
//        resultVo.setDeliveryStatus(queryVo.getStatus());

        if (CollUtil.isNotEmpty(queryVo.getContent())) {
            List<LogisticsResultListVo> list = CollUtil.newArrayList();
            queryVo.getContent().forEach(i -> {
                LogisticsResultListVo listVo = new LogisticsResultListVo();
//                listVo.setTime(i.getTime());
                listVo.setStatus(i.getStatus());
                list.add(listVo);
            });
            resultVo.setList(list);
        }
        return resultVo;
    }

    /**
     * 获取快递缓存
     *
     * @return JSONObject
     */
    private JSONObject getCache(String redisKey) {
        Object data = redisUtil.get(redisKey);
        if (ObjectUtil.isNotNull(data)) {
            return JSONObject.parseObject(data.toString());
        }
        return null;
    }

    /**
     * 获取快递缓存
     *
     * @param data JSONObject 需要保存的数据
     */
    private void saveCache(String redisKey, JSONObject data) {
        redisUtil.set(redisKey, data.toJSONString(), 1800L, TimeUnit.SECONDS);
    }

    /**
     * 获取快递缓存
     *
     * @param data JSONObject 检测返回的结果
     */
    private void checkResult(JSONObject data) {
        if (!data.getString("status").equals("0")) {
            throw new CrmebException(data.getString("msg"));
        }
    }
}

